<template>
  <div class="x-code-mirror">
    <div v-if="showToolBar" class="toolbar">
      <toolbar :config="config"
               :languages="filterLanguages"
               :themes="themes"
               @changeLanguage="ensureLanguageCode"/>
    </div>
    <div class="code-editor">
        <codemirror
            v-if="!loading"
            v-model="code"
            placeholder="Code goes here..."
            :style="{ height: config.height }"
            :autofocus="config.autofocus"
            :indent-with-tab="config.indentWithTab"
            :tab-size="config.tabSize"
            :disabled="config.disabled"
            :extensions="extensions"
        />
      </div>
  </div>
</template>

<script lang="ts" setup >

import {computed, defineComponent, onBeforeMount, onMounted, reactive, ref, shallowRef, watch} from 'vue'
import { Codemirror } from 'vue-codemirror'
import { oneDark } from '@codemirror/theme-one-dark'
import languages from "./languages";
import Toolbar from "@/components/XCodeMirror/ToolBar.vue";
import themes from './themes'
import useStore from "@/store";
import {PropType} from "@vue/runtime-core";
import {useRoute} from "vue-router";
const {webConfig}  = useStore()
const props =  defineProps({
  showToolBar : {
    type:Boolean,
    default: true
  },
  defaultConfig:{
    type: Object as () => tConfig,
    default:() => ({})
  },
  languageList:{
    type: Object,
    default:() => ({})
  }
})
const loading = ref<Boolean>(true)
const uuid = ref();
onBeforeMount(() =>{
  const {path} = useRoute();
  // const lastIndex = path.lastIndexOf("/");
  // if (lastIndex !== -1) {
  //   uuid.value = path.substring(lastIndex + 1);
  // } else {
  //   uuid.value = "__"
  // }
  //console.log("路径" + path)
  uuid.value = path
  config.language = props.defaultConfig?.language ||  config.language
  config.height = props.defaultConfig?.height || config.height
  config.disabled = props.defaultConfig?.disabled || config.disabled

  if(props.defaultConfig?.code){
    sessionStorage.setItem(config.language + "_" + uuid.value + "_code",props.defaultConfig.code)
  }
  ensureLanguageCode(config.language as string)
});

function isObjectEmpty(obj) {
  if(obj == null)
    return true;
  return Object.keys(obj).length === 0 && obj.constructor === Object;
}
const filterLanguages = computed(() => {

  if(!isObjectEmpty(props.languageList)){
    return props.languageList;
  }
  return languages;
})
const code = computed({
  get(){
   // console.log("触发get" + uuid.value)
  //  console.log(sessionStorage.getItem(config.language + "_" + uuid.value + "_code"))
    return sessionStorage.getItem(config.language + "_" + uuid.value + "_code") || langCodeMap.get(config.language as string).code
  },
  set(value){
   // console.log("触发set")
    sessionStorage.setItem(config.language + "_" + uuid.value + "_code",value)
  }
})
/**
 * 调用此方法获取当前编辑器信息
 */
const getCodeMirrorInfo = () => {
  return {
    code:sessionStorage.getItem(config.language + "_" + uuid.value + "_code") || langCodeMap.get(config.language as string).code,
    lang:config.language
  }
}
defineExpose({
  getCodeMirrorInfo
})

/**
 * 配置
 */
type tConfig  =  {
  code?:string,
  disabled?:boolean,
  indentWithTab?:boolean,
  tabSize?:number,
  autofocus?:boolean,
  height?:string,
  language?:string,
  theme?: 'oneDark' | 'default'
}
const config = reactive<tConfig>({

  code:"",
  //可禁用
  disabled: false,
  //启动tab键
  indentWithTab: true,
  //缩进
  tabSize: 4,
  //自动聚焦
  autofocus: true,
  //高度
  height: '100%',
  //选中的语言
  language: 'C++',
  //风格
  theme : webConfig.isDarkColor ? 'oneDark' : 'default'
})
webConfig.$subscribe((mutation, state) => {
  config.theme = state.isDarkColor ? 'oneDark' : 'default'
})
const langCodeMap = reactive(new Map<string, { code: string; language: () => any }>())

//语言、UI配置
const extensions = computed( () =>{
  const result = []
  if(currentLangCode.value)
    result.push(currentLangCode.value.language())
  if(currentTheme.value)
    result.push(currentTheme.value)
  return result;
})
const currentLangCode = computed(() => langCodeMap.get(config.language as string)!)
const currentTheme = computed(() => {
  return config.theme == 'oneDark' ? themes[config.theme] : void 0
})

/**
 * 初始化语言
 * @param targetLanguage 语言名称
 */
const ensureLanguageCode = async (targetLanguage : string) => {

  console.log(targetLanguage)
  loading.value = true
  const delayPromise = () => new Promise((resolve) => window.setTimeout(resolve, 260))
  if (langCodeMap.has(targetLanguage)) {
    await delayPromise()
  } else {
    const [result] = await Promise.all([languages[targetLanguage.toLowerCase()](), delayPromise()])
    // console.log(result.default)
    if(!isObjectEmpty(props.languageList))
      result.default.code = props.languageList[targetLanguage]?.code || result.default.code
    langCodeMap.set(targetLanguage, result.default)
  }
  loading.value = false
}

</script>

<style lang="scss">
/**
外层容器高度要确定
 */
.x-code-mirror{
  height: 100%;
  display: flex;
  flex-direction: column;
  .code-editor{
    flex: 1;
    overflow: hidden;
  }
}
</style>
